package com.learn.utils;

import cn.hutool.extra.spring.SpringUtil;
import com.learn.config.JwtConfig;
import io.jsonwebtoken.*;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.util.Base64Utils;

import java.nio.charset.StandardCharsets;

/**
 * Jwt token工具类
 *
 * @author zhouhaofeng
 * @version 2022/4/6 0006
 * @see [相关类/方法]
 * @since [cloud-phone-service]
 */
public class JwtUtil
{
	/**
	 * jwt的subject内容（用户ID字段）
	 */
	private static final String SUB = "sub";

	/**
	 * 用户账号（操作员/终端用户）字段
	 */
	private static final String ACCOUNT = "account";

	/**
	 * 租户ID字段
	 */
	private static final String TENANT_ID = "tenantId";

	/**
	 * 渠道字段
	 */
	private static final String CHANNEL = "channel";

	/**
	 * 登录方式字段
	 */
	private static final String LOGIN_METHOD = "loginMethod";

	/**
	 * 客户端类型
	 */
	private static final String CLIENT_TYPE = "clientType";

	private JwtUtil ()
	{

	}

	/**
	 * 根据token信息解析sub参数（终端用户ID/操作员ID）
	 *
	 * @param token jwt token
	 * @return 终端用户ID/操作员ID
	 */
	public static String parseSub (String token)
	{
		String encodedKey = getEncodedKey ();
		Claims claims = Jwts.parser ().setSigningKey (encodedKey).parseClaimsJws (token).getBody ();
		return claims.get (SUB) == null ? "" : claims.get (SUB).toString ();
	}

	/**
	 * 根据token信息解析account参数（终端用户账号/操作员账号）
	 *
	 * @param token jwt token
	 * @return 终端用户账号/操作员账号
	 */
	public static String parseAccount (String token)
	{
		String encodedKey = getEncodedKey ();
		Claims claims = Jwts.parser ().setSigningKey (encodedKey).parseClaimsJws (token).getBody ();
		return claims.get (ACCOUNT) == null ?
				"" :
				AESUtil.decryptEnd (claims.get (ACCOUNT).toString (), getAccountSecretKey ());
	}

	/**
	 * 根据token信息解析tenantId参数（租户ID）
	 *
	 * @param token jwt token
	 * @return 租户ID
	 */
	public static String parseTenantId (String token)
	{
		String encodedKey = getEncodedKey ();
		Claims claims = Jwts.parser ().setSigningKey (encodedKey).parseClaimsJws (token).getBody ();
		return claims.get (TENANT_ID) == null ? "" : claims.get (TENANT_ID).toString ();
	}

	/**
	 * 根据token信息解析channel参数（渠道号）
	 *
	 * @param token jwt token
	 * @return 渠道号
	 */
	public static String parseChannel (String token)
	{
		String encodedKey = getEncodedKey ();
		Claims claims = Jwts.parser ().setSigningKey (encodedKey).parseClaimsJws (token).getBody ();
		return claims.get (CHANNEL) == null ? "" : claims.get (CHANNEL).toString ();
	}

	/**
	 * 解析Token
	 *
	 * @param token     token信息
	 * @param secretKey base64编码前的密钥
	 * @return token解析结果
	 * @throws ExpiredJwtException      a JWT was accepted after it expired and must be rejected.
	 * @throws UnsupportedJwtException  a JWT in a particular format/configuration that does not match the format expected by the application.
	 * @throws MalformedJwtException    JWT was not correctly constructed异常
	 * @throws SignatureException       Exception indicating that either calculating a signature or verifying an existing signature of a JWT failed.
	 * @throws IllegalArgumentException Thrown to indicate that a method has been passed an illegal or inappropriate argument.
	 */
	public static Claims parseToken (String token, String secretKey)
			throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException,
			IllegalArgumentException

	{
		return Jwts.parser ().setSigningKey (getEncodedKey (secretKey)).parseClaimsJws (token).getBody ();
	}

	/**
	 * 从配置文件里读取jwt的私钥
	 *
	 * @return 读取jwt的私钥
	 */
	private static String getJwtPrivateKey ()
	{
		JwtConfig jwtConfig = SpringUtil.getBean (JwtConfig.class);
		return jwtConfig.getKey () == null ? "" : jwtConfig.getKey ();
	}

	/**
	 * 获取base64编码后密钥
	 *
	 * @return base64编码后密钥
	 */
	private static String getEncodedKey ()
	{
		String key = getJwtPrivateKey ();
		return Base64Utils.encodeToString (key.getBytes (StandardCharsets.UTF_8));
	}

	/**
	 * 获取base64编码后密钥
	 *
	 * @param secretKey 编码前的密钥
	 * @return base64编码后密钥
	 */
	private static String getEncodedKey (String secretKey)
	{
		return Base64Utils.encodeToString (secretKey.getBytes (StandardCharsets.UTF_8));
	}

	/**
	 * 从配置文件里读取账户加密密钥
	 *
	 * @return 读取账户加密密钥
	 */
	private static String getAccountSecretKey ()
	{
		JwtConfig jwtConfig = SpringUtil.getBean (JwtConfig.class);
		return jwtConfig.getAccountSecretKey () == null ? "" : jwtConfig.getAccountSecretKey ();
	}

	@Data
	@Accessors (chain = true)
	public static class TokenInfo
	{
		/**
		 * 用户ID
		 */
		private String subId;

		/**
		 * 用户账号
		 */
		private String account;

		/**
		 * 登录方式（适用于用户面）
		 */
		private String loginMethod;

		/**
		 * 客户端类型（适用于用户面）
		 */
		private String clientType;

		/**
		 * 登录渠道（适用于管理面）
		 */
		private String channel;

		/**
		 * 租户Id
		 */
		private String tenantId;
	}

	/**
	 * 解析Token获取Token信息
	 * 包括用户账号、用户ID、登录方式（适用于用户面）、客户端类型（适用于用户面）、登录渠道（适用于管理面）、租户id
	 *
	 * @param token jwt
	 * @return Token信息
	 */
	public static TokenInfo parseTokenInfo (String token)
	{
		String encodedKey = getEncodedKey ();
		Claims claims = Jwts.parser ().setSigningKey (encodedKey).parseClaimsJws (token).getBody ();
		return new TokenInfo ().setSubId (claims.get (SUB) == null ? "" : claims.get (SUB).toString ()).setAccount (
						claims.get (ACCOUNT) == null ?
								"" :
								AESUtil.decryptEnd (claims.get (ACCOUNT).toString (), getAccountSecretKey ()))
				.setLoginMethod (claims.get (LOGIN_METHOD) == null ? "" : claims.get (LOGIN_METHOD).toString ())
				.setClientType (claims.get (CLIENT_TYPE) == null ? "" : claims.get (CLIENT_TYPE).toString ())
				.setChannel (claims.get (CHANNEL) == null ? "" : claims.get (CHANNEL).toString ())
				.setTenantId (claims.get (TENANT_ID) == null ? "" : claims.get (TENANT_ID).toString ());
	}
}
